#include "engine.h"
#include "internal_types.h"
#include <map>
#include <string>
#include <set>
using namespace std;

#include <gli/gli.hpp>
#include <gli/gtx/gl_texture2d.hpp>
#include <GL/glew.h>

namespace HW
{

using namespace Resource;

map<string,TTexture> lFileTextures;
set<TTexture> lTextures;

TTexture CreateTexture(int width, int height, int format)
{
	AppLog ( "Creating texture." );
	_i_Texture* lNewTexture = new _i_Texture();
	lNewTexture -> width = width;
	lNewTexture -> height = height;
	glGenTextures ( 1 , &lNewTexture->id );
	glBindTexture ( GL_TEXTURE_2D , lNewTexture->id );
	switch ( format )
	{
		case TEXTURE_ALPHA8:
			glTexImage2D ( GL_TEXTURE_2D , 0 , GL_ALPHA8 , width , height , 0 , GL_ALPHA , GL_FLOAT , NULL );
			break;
		case TEXTURE_LUMINANCE8:
			glTexImage2D ( GL_TEXTURE_2D , 0 , GL_LUMINANCE8 , width , height , 0 , GL_LUMINANCE , GL_FLOAT , NULL );
			break;
		case TEXTURE_RGB8:
			glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB8 , width , height , 0 , GL_RGB , GL_FLOAT , NULL );
			break;
		case TEXTURE_RGBA8:
			glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA8 , width , height , 0 , GL_RGBA , GL_FLOAT , NULL );
			break;
		case TEXTURE_RGBA16:
			glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA16 , width , height , 0 , GL_RGBA , GL_FLOAT , NULL );
			break;
		case TEXTURE_RGBA32:
			glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA32F , width , height , 0 , GL_RGBA , GL_FLOAT , NULL );
			break;
		case TEXTURE_RG11B10:
			glTexImage2D ( GL_TEXTURE_2D , 0 , GL_R11F_G11F_B10F , width , height , 0 , GL_RGBA , GL_FLOAT , NULL );
			break;
		case TEXTURE_DEPTH24:
			glTexImage2D ( GL_TEXTURE_2D , 0 , GL_DEPTH_COMPONENT24 , width , height , 0 , GL_DEPTH_COMPONENT , GL_FLOAT , NULL );
			break;
		case TEXTURE_FLOAT:
			glTexImage2D ( GL_TEXTURE_2D , 0 , GL_LUMINANCE16F_ARB , width , height , 0 , GL_LUMINANCE , GL_FLOAT , NULL );
			break;
	}

	lTextures.insert ( (TTexture)(lNewTexture) );

	return (TTexture)(lNewTexture);
}

TTexture LoadTexture(const_str name)
{
	string lPath = AbstractPath ( name );

	if ( lFileTextures.find ( lPath ) != lFileTextures.end() )
	{
		return lFileTextures[lPath];
	}

	AppLog ( string("Loading texture: \"" + lPath + "\"").c_str() );

	extern bool _i_FileExist ( const_str path );
	if ( !_i_FileExist ( lPath.c_str() ) )
	{
		AppLog ( string("Failed to load texture: \"" + lPath + "\"").c_str() );
		return NULL;
	}

	_i_Texture* lNewTexture = new _i_Texture();
	
	lNewTexture -> id = gli::createTexture2D ( lPath );

	lFileTextures[lPath] = (TTexture)(lNewTexture);

	lTextures.insert ( (TTexture)(lNewTexture) );

	return (TTexture)(lNewTexture);
}

void ClampTexture(TTexture texture, int x, int y, int z)
{
}

void TFilter(int mode)
{
}

void AFilter(int mode)
{
}

int MaxAFilter(void)
{
	GLfloat fLargest;
	glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
	return (int)fLargest;
}

long TextureWidth(TTexture texture)
{
	return ((_i_Texture*)(texture))->width;
}

long TextureHeight(TTexture texture)
{
	return ((_i_Texture*)(texture))->height;
}

void TextureFilter(TTexture texture, int filter)
{
}

str TextureName(TTexture texture)
{
	return "";
}

void BindTexture(TTexture texture, int texunit)
{
	if ( texunit > 15 )
		return;

	glActiveTexture ( GL_TEXTURE0 + texunit );
	glBindTexture ( GL_TEXTURE_2D , ((_i_Texture*)(texture))->id );
}

void FreeTexture(TTexture texture)
{
	AppLog ( "Freeing texture." );
	_i_Texture* lTex = (_i_Texture*)texture;
	glDeleteTextures ( 1 , &lTex->id );

	lTextures.erase ( texture );

	// TODO: Should remove the texture entry in the filetexture map.
}

int MaxTextureUnits(void)
{
	GLint lQueryI;
	glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &lQueryI);
	return lQueryI;
}

void SetTextureQuality(int quality)
{
}

void _i_UnInitTextures ( void )
{
	while ( !lTextures.empty() )
		FreeTexture ( *lTextures.begin() );
}

}